#include "acceptor.h"

#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <memory>

#include "global_env.h"
#include "util.h"

Acceptor::Acceptor(EventLoop* loop):
    loop_(loop),
    listenChannel_(new Channel(loop))
{
    SocketBindListen(PORT, LISTENQ);
    listenChannel_->SetFd(listenFd_);
}

Acceptor::~Acceptor(){}

void Acceptor::Listen()
{
    listenChannel_->SetReadCallBack(std::bind(&Acceptor::AcceptConnection, this));
    listenChannel_->EnableReading();
}

void Acceptor::AcceptConnection()
{
    // 建立连接
    struct sockaddr_in client_addr;
    memset(&client_addr, 0, sizeof(struct sockaddr_in));
    socklen_t client_addr_len = 0;
    int acceptFd = 0;

    acceptFd = accept(listenFd_, (struct sockaddr *) &client_addr, &client_addr_len);
    CHK_RET(acceptFd >= 0);
    // 执行Server传入的CB 
    if(connectionCB_){
        connectionCB_(acceptFd);
    }
}

int Acceptor::SetNonBlocking(int fd) 
{
    int old_option = fcntl(fd, F_GETFL, 0);
    assert(old_option != -1);

    assert(fcntl(fd, F_SETFL, old_option | O_NONBLOCK) == 0);
    return old_option;
}


bool Acceptor::SocketBindListen(int port, int listenNum) 
{
    CHK_RET_VLE( (port>=1025 && port<=65534), false);

    listenFd_ = socket(AF_INET, SOCK_STREAM, 0);
    assert(listenFd_ != -1);
    // CHK_RET_VLE(listenFd_!=-1, false);

    // 消除bind时"Address already in use"错误
    int optval = 1;
    int succ = setsockopt(listenFd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
    CHK_RET_VLE(succ != -1, false);

    // 设置服务器IP和Port，和监听描述副绑定
    struct sockaddr_in server_addr;
    memset((char *) &server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons((unsigned short) port);

    succ = bind(listenFd_, (struct sockaddr *) &server_addr, sizeof(server_addr));
    CHK_RET_VLE(succ!= -1, false);

    listen(listenFd_, listenNum);
    CHK_RET_VLE( succ!= -1, false);
    // 任何环节出错都会导致由析构函数进行关闭连接套接字

    SetNonBlocking(listenFd_);
    return true;
}


